using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerMovementState : IState
{
    protected PlayerMovementMachine StateMachine;
    protected PlayerGroundedData movementData;
    protected PlayerAirborneData airborneData;

    public PlayerMovementState(PlayerMovementMachine stateMachine)
    {
        StateMachine = stateMachine;
        movementData = StateMachine.Player.Data.PlayerGroundedData;
        airborneData = StateMachine.Player.Data.PlayerAirborneData;
        InitiationData();
    }

#region IState Methods
    public virtual void Enter()
    {
        Debug.Log($"State: {GetType().Name}");
        AddInputActionsCallBacks();
    }

    public virtual void Exit()
    {
        RemoveInputActionsCallBacks();
    }

    public virtual void HandleInput()
    {
        ReadMovementInput();
    }

    public virtual void Update()
    {
    }

    public virtual void PhysicUpdate()
    {
        Move();
    }

    public virtual void OnAnimationEnterEvent()
    {
    }

    public virtual void OnAnimationExitEvent()
    {
    }

    public virtual void OnAnimationTransitionEvent()
    {
    }

    public void OnTriggerEnter(Collider collider)
    {
        if (StateMachine.Player.PlayerLayerData.ContainsGroundLayer(collider.gameObject.layer))
        {
            OnContactWithGround();
            return;
        }
    }

    public void OnTriggerExit(Collider collider)
    {
        if (StateMachine.Player.PlayerLayerData.ContainsGroundLayer(collider.gameObject.layer))
        {
            OnContactWithGroundExited();
            return;
        }
    }
#endregion

#region Main Methods
    private void InitiationData()
    {
        SetBaseRotation();
    }

    private void ReadMovementInput()
    {
        StateMachine.ReusableData.InputMovement =
            StateMachine.Player.PlayerInput.PlayerActions.Movement.ReadValue<Vector2>();
    }

    private void Move()
    {
        if (StateMachine.ReusableData.InputMovement == Vector2.zero ||
            StateMachine.ReusableData.MovementSpeedModify == 0f)
            return;

        Vector3 movementDirection = GetMovementInputDirection();

        float targetRotationYAngle = Rotate(movementDirection);
        Vector3 targetRotationDirection = GetTargetRotationDirection(targetRotationYAngle);

        float movementSpeed = GetMovementSpeed();

        Vector3 currentPlayerHorizontalVelocity = GetHorizontalPlayerVelocity();

        StateMachine.Player.Rigidbody.AddForce(
            movementSpeed * targetRotationDirection - currentPlayerHorizontalVelocity, ForceMode.VelocityChange);
    }

    private float Rotate(Vector3 direction)
    {
        float directionAngle = UpdateTargetRotation(direction);

        RotateTowardsTargetRotation();

        return directionAngle;
    }

    private void UpdateTargetRotationData(float targetAngle)
    {
        StateMachine.ReusableData.CurrentTargetRotation.y = targetAngle;
        StateMachine.ReusableData.DampedTargetRotationPassedTime.y = 0f;
    }

    private float GetDirectionAngle(Vector3 direction)
    {
        float directionAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
        if (directionAngle < 0)
            directionAngle += 360f;
        if (directionAngle > 360f)
            directionAngle -= 360f;

        return directionAngle;
    }

    private float AddCameraRotateToAngle(float angle)
    {
        angle += StateMachine.Player.MainCameraTransform.eulerAngles.y;
        if (angle > 360f)
            angle -= 360f;
        if (angle < 0f)
            angle += 360f;

        return angle;
    }
#endregion

#region Reuseable Methods
    protected void StartAnimation(int animationHash)
    {
        StateMachine.Player.Animator.SetBool(animationHash, true);
    }

    protected void StopAnimation(int animationHash)
    {
        StateMachine.Player.Animator.SetBool(animationHash, false);
    }

    protected float UpdateTargetRotation(Vector3 direction, bool shouldConsiderCamera = true)
    {
        // 计算移动的方向
        float directionAngle = GetDirectionAngle(direction);

        // 计算摄像机附带的方向
        if (shouldConsiderCamera)
        {
            directionAngle = AddCameraRotateToAngle(directionAngle);
        }

        if (Math.Abs(directionAngle - StateMachine.ReusableData.CurrentTargetRotation.y) > Mathf.Epsilon)
        {
            UpdateTargetRotationData(directionAngle);
        }

        return directionAngle;
    }

    protected void SetBaseRotation()
    {
        StateMachine.ReusableData.RotationData = movementData.BaseRotationData;
        StateMachine.ReusableData.TimeToReachTargetRotation =
            StateMachine.ReusableData.RotationData.TargetRotationReachTime;
    }

    protected Vector3 GetMovementInputDirection()
    {
        return new Vector3(StateMachine.ReusableData.InputMovement.x, 0, StateMachine.ReusableData.InputMovement.y);
    }

    protected float GetMovementSpeed()
    {
        return StateMachine.Player.Data.PlayerGroundedData.BaseSpeed * StateMachine.ReusableData.MovementSpeedModify *
               StateMachine.ReusableData.MovementSpeedModifyOnSlope;
    }

    protected Vector3 GetHorizontalPlayerVelocity()
    {
        Vector3 playerHorizontalVelocity = StateMachine.Player.Rigidbody.velocity;
        playerHorizontalVelocity.y = 0;
        return playerHorizontalVelocity;
    }

    protected Vector3 GetVerticalPlayerVelocity()
    {
        Vector3 playerVerticalPlayerVelocity = StateMachine.Player.Rigidbody.velocity;
        return new Vector3(0, playerVerticalPlayerVelocity.y, 0);
    }

    protected void RotateTowardsTargetRotation()
    {
        float currentYAngle = StateMachine.Player.Rigidbody.rotation.eulerAngles.y;
        if (Math.Abs(currentYAngle - StateMachine.ReusableData.CurrentTargetRotation.y) < Mathf.Epsilon)
            return;

        float smoothYAngle = Mathf.SmoothDampAngle(currentYAngle, StateMachine.ReusableData.CurrentTargetRotation.y,
            ref StateMachine.ReusableData.DampedTargetRotationCurrentVelocity.y,
            StateMachine.ReusableData.TimeToReachTargetRotation.y -
            StateMachine.ReusableData.DampedTargetRotationPassedTime.y);

        StateMachine.ReusableData.DampedTargetRotationPassedTime.y += Time.deltaTime;

        Quaternion targetQuaternion = Quaternion.Euler(0f, smoothYAngle, 0);
        StateMachine.Player.Rigidbody.MoveRotation(targetQuaternion);
    }

    protected Vector3 GetTargetRotationDirection(float targetYAngle)
    {
        return Quaternion.Euler(0, targetYAngle, 0) * Vector3.forward;
    }

    protected void ResetVelocity()
    {
        StateMachine.Player.Rigidbody.velocity = Vector3.zero;
    }

    protected void ResetVerticalVelocity()
    {
        StateMachine.Player.Rigidbody.velocity = GetHorizontalPlayerVelocity();
    }

    protected virtual void AddInputActionsCallBacks()
    {
        StateMachine.Player.PlayerInput.PlayerActions.WalkToggle.started += OnWalkToggleStated;
    }

    protected virtual void RemoveInputActionsCallBacks()
    {
        StateMachine.Player.PlayerInput.PlayerActions.WalkToggle.started -= OnWalkToggleStated;
    }

    protected virtual void DecelerateHorizontally()
    {
        Vector3 playerHorizontalVelocity = GetHorizontalPlayerVelocity();
        StateMachine.Player.Rigidbody.AddForce(-playerHorizontalVelocity * StateMachine.ReusableData.DecelerationForce,
            ForceMode.Acceleration);
    }

    protected virtual void DecelerateVertically()
    {
        Vector3 playerVerticalVelocity = GetVerticalPlayerVelocity();
        StateMachine.Player.Rigidbody.AddForce(-playerVerticalVelocity * StateMachine.ReusableData.DecelerationForce,
            ForceMode.Acceleration);
    }

    protected virtual bool IsMovingHorizontally(float velocityMagnitude = 0.1f)
    {
        Vector3 playerHorizontalVelocity = GetHorizontalPlayerVelocity();
        Vector2 velocity = new Vector2(playerHorizontalVelocity.x, playerHorizontalVelocity.z);
        return velocity.magnitude > velocityMagnitude;
    }

    protected virtual void OnContactWithGround()
    {
    }

    protected virtual void OnContactWithGroundExited()
    {
    }

    protected bool IsMovingUp(float minVelocity = 0.1f)
    {
        return GetVerticalPlayerVelocity().y > minVelocity;
    }

    protected bool IsMovingDown(float minVelocity = 0.1f)
    {
        return GetVerticalPlayerVelocity().y < minVelocity;
    }
#endregion

#region Input Methods
    protected virtual void OnWalkToggleStated(InputAction.CallbackContext context)
    {
        StateMachine.ReusableData.ShouldWalk = !StateMachine.ReusableData.ShouldWalk;
    }
#endregion
}